﻿using System;
using System.Collections.Generic;
using System.Text;

public class ViewService
{
    private const char k_X = 'X';
    private const char k_O = 'O';
    private const char k_YesCapsAnswer = 'Y';
    private const char k_YesNonCapsAnswer = 'y';
    private const char k_NoCapsAnswer = 'N';
    private const char k_NoNonAnswer = 'n';
    private const int k_FirstCol = 1;
    private const char k_QuitChar = 'q';
    private const int k_UserModeInt = 1;
    private const int k_PcModeInt = 2;
    private const char k_UserModeChar = '1';
    private const char k_PcModeChar = '2';
    private const int k_MinBoardLegitVal = 4;
    private const int k_MaxBoardLegitVal = 8;
    private const int k_CellViewSize = 5;
    private const char k_BorderChar = '=';
    private const char k_Space = ' ';
    private const string k_TripleSpace = "  ";
    private const string k_PipeLeft = "| ";
    private const char k_Pipe = '|';
    private int m_BoardViewRowSize = 0;
    private int m_BoardViewColSize = 0;
    private int m_GameMode = 0;
    private StringBuilder m_StringBuilder = null;

    public int BoardViewRowSize
    {
        get { return m_BoardViewRowSize; }
        set { m_BoardViewRowSize = value; }
    }

    public int BoardViewColSize
    {
        get { return m_BoardViewColSize; }
        set { m_BoardViewColSize = value; }
    }

    public int GameMode
    {
        get { return m_GameMode; }
        set { m_GameMode = value; }
    }

    public enum eErrorMessage
    {
        ColumnIsFull = 0,
        InvalidBoardSize,
        InvalidGameMode,
        InvalidKeyPressed,
        BoardIsFull
    }

    public void PrintBoard(BoardService.eCell[,] i_BoardCellArray)
    {
        Ex02.ConsoleUtils.Screen.Clear();
        m_StringBuilder.Remove(0, m_StringBuilder.Length);
        
        for (int CurrentColIndex = 1; CurrentColIndex <= m_BoardViewColSize; CurrentColIndex++)
        {
            m_StringBuilder.Append(k_TripleSpace);
            m_StringBuilder.Append(CurrentColIndex);
            m_StringBuilder.Append(k_Space);
        }

        m_StringBuilder.Append(System.Environment.NewLine);

        for (int CurrentRowIndex = 0; CurrentRowIndex < m_BoardViewRowSize; CurrentRowIndex++)
        {
            appendRowWithDiscs(i_BoardCellArray, CurrentRowIndex);
            m_StringBuilder.Append(System.Environment.NewLine);
            appendBorderRows();
            m_StringBuilder.Append(System.Environment.NewLine);
        }

        m_StringBuilder.Append(System.Environment.NewLine);

        System.Console.WriteLine(m_StringBuilder);
    }

    private void appendBorderRows()
    {
        string BorderRow = new string(k_BorderChar, k_CellViewSize * (m_BoardViewColSize - 1));
        m_StringBuilder.Append(BorderRow);
    }

    private void appendRowWithDiscs(BoardService.eCell[,] i_BoardCellArray, int i_CurrentRowIndex)
    {
        for (int CurrentColIndex = 0; CurrentColIndex < m_BoardViewColSize; CurrentColIndex++)
        {
            m_StringBuilder.Append(k_PipeLeft);
            BoardService.eCell CurrentCell = i_BoardCellArray[i_CurrentRowIndex, CurrentColIndex];

            if (CurrentCell.Equals(BoardService.eCell.X))
            {
                m_StringBuilder.Append(k_X);
            }
            else if (CurrentCell.Equals(BoardService.eCell.O))
            {
                m_StringBuilder.Append(k_O);
            }
            else if (CurrentCell.Equals(BoardService.eCell.Empty))
            {
                m_StringBuilder.Append(k_Space);
            }

            m_StringBuilder.Append(k_Space);
        }

        m_StringBuilder.Append(k_Pipe);
    }

    public void GameUserMenuSetup()
    {
        gameBoardSizeSetup();
        initStringBuilder();
        gameModeSetup();
    }

    private void initStringBuilder()
    {
        m_StringBuilder = new StringBuilder((k_CellViewSize * m_BoardViewColSize * m_BoardViewRowSize) + 1);
    }

    private void gameBoardSizeSetup()
    {
        bool RowSetupResult = false;
        bool ColSetupResult = false;
        bool GameBoardSizeSetupResult = false;

        Ex02.ConsoleUtils.Screen.Clear();

        while (GameBoardSizeSetupResult == false)
        {
            System.Console.WriteLine(@"Please enter board rows size between {0} to {1}:" + System.Environment.NewLine, k_MinBoardLegitVal, k_MaxBoardLegitVal);
            char GameBoardRowSizeChar = System.Console.ReadKey().KeyChar;

            RowSetupResult = int.TryParse(GameBoardRowSizeChar.ToString(), out m_BoardViewRowSize);
            System.Console.WriteLine(Environment.NewLine);
            
            System.Console.WriteLine(@"Please enter board columns size between {0} to {1}:" + System.Environment.NewLine, k_MinBoardLegitVal, k_MaxBoardLegitVal);
            char GameBoardColSizeChar = System.Console.ReadKey().KeyChar;
            ColSetupResult = int.TryParse(GameBoardColSizeChar.ToString(), out m_BoardViewColSize);

            if (ColSetupResult == true && RowSetupResult == true)
            {
                GameBoardSizeSetupResult = validateBoardSizeInput();
            }

            if (GameBoardSizeSetupResult == false)
            {
                ErrorHandler(eErrorMessage.InvalidBoardSize);
                continue;
            }
        }

        System.Threading.Thread.Sleep(500);
    }

    private bool validateBoardSizeInput()
    {
        bool result = true;

        if (m_BoardViewColSize < k_MinBoardLegitVal || m_BoardViewColSize > k_MaxBoardLegitVal)
        {
            result = false;
        }
        else if (m_BoardViewRowSize < k_MinBoardLegitVal || m_BoardViewRowSize > k_MaxBoardLegitVal)
        {
            result = false;
        }

        return result;
    }

    private void gameModeSetup()
    {
        bool result = false;

        while (result == false)
        {
            Ex02.ConsoleUtils.Screen.Clear();

            System.Console.WriteLine(
@"Select game mode:
1. Player vs Player
2. Player vs PC" + System.Environment.NewLine);

            char PlayerMoveChar = System.Console.ReadKey().KeyChar;

            if (PlayerMoveChar.Equals(k_UserModeChar))
            {
                result = true;
                m_GameMode = k_UserModeInt;
            }
            else if (PlayerMoveChar.Equals(k_PcModeChar))
            {
                result = true;
                m_GameMode = k_PcModeInt;
            }
            else
            {
                ErrorHandler(eErrorMessage.InvalidGameMode);
                continue;
            }
        }
    }

    public char PlayRound()
    {
        bool PlayerFinishedRound = false;
        char? PlayerMoveChar = null;

        while (PlayerFinishedRound == false)
        {
            PlayerMoveChar = System.Console.ReadKey().KeyChar;

            if (validatePlayerMove(PlayerMoveChar.Value))
            {
                PlayerFinishedRound = true;
                break;
            }
            else
            {
                ErrorHandler(eErrorMessage.InvalidKeyPressed);
            }
        }

        return PlayerMoveChar.Value;
    }

    private bool validatePlayerMove(char i_CurrentPlayerMoveChar)
    {
        bool result = true;
        int CurrentPlayerMoveNumberValue = (int) char.GetNumericValue(i_CurrentPlayerMoveChar);
        
        if (i_CurrentPlayerMoveChar.Equals(k_QuitChar))
        {
            result = true;
        }
        else if (CurrentPlayerMoveNumberValue > m_BoardViewColSize)
        {
            result = false;
        }
        else if (CurrentPlayerMoveNumberValue < k_FirstCol)
        {
            result = false;
        }
        else if (char.IsDigit(i_CurrentPlayerMoveChar) == false && i_CurrentPlayerMoveChar != k_QuitChar)
        {
            result = false;
        }

        return result;
    }

    public void Winner(int i_CurrentPlayerIndex, int i_FirstPlayerScore, int i_SecondPlayerScore)
    {
        Ex02.ConsoleUtils.Screen.Clear();
        System.Console.WriteLine(
@"*************************
*    The Winner is:     *
*                       *
*       Player {0}        *
*                       *
*  Player 1 Score: {1}    *
*  Player 2 Score: {2}    *
*************************" + System.Environment.NewLine,
                           i_CurrentPlayerIndex + 1, 
                           i_FirstPlayerScore, 
                           i_SecondPlayerScore);
        System.Threading.Thread.Sleep(3000);
    }
    
    public void AnotherGameMenu(ref bool io_EndOfGame)
    {
        bool AnotherGameAnswer = false;

        while (AnotherGameAnswer == false)
        {
            Ex02.ConsoleUtils.Screen.Clear();
            System.Console.WriteLine(@"Do you want to play another game? ({0}/{1})" + System.Environment.NewLine, k_YesCapsAnswer, k_NoCapsAnswer);
            ConsoleKeyInfo key = System.Console.ReadKey();
            char PlayerAnswer = key.KeyChar;

            if (PlayerAnswer.Equals(k_YesCapsAnswer) || PlayerAnswer.Equals(k_YesNonCapsAnswer))
            {
                io_EndOfGame = false;
                AnotherGameAnswer = true;
            }
            else if (PlayerAnswer.Equals(k_NoCapsAnswer) || PlayerAnswer.Equals(k_NoNonAnswer))
            {
                io_EndOfGame = true;
                AnotherGameAnswer = true;
            }
            else
            {
                System.Console.WriteLine(System.Environment.NewLine + @"Please enter {0}/{1}" + System.Environment.NewLine, k_YesCapsAnswer, k_NoCapsAnswer);
                System.Threading.Thread.Sleep(300);
            }
        }
    }

    public void ErrorHandler(eErrorMessage errorMessage)
    {
        switch (errorMessage)
        {
            case eErrorMessage.InvalidBoardSize:
                {
                    Ex02.ConsoleUtils.Screen.Clear();
                    System.Console.WriteLine(@"Invalid game board rows / columns size. Enter values between {0} to {1}" + System.Environment.NewLine, k_MinBoardLegitVal, k_MaxBoardLegitVal);
                    break;
                }

            case eErrorMessage.InvalidGameMode:
                {
                    clearErrorMessageLine();
                    System.Console.WriteLine(@"Invalid game mode chosen, please choose option {0} or {1}." + System.Environment.NewLine, k_UserModeInt, k_PcModeInt);
                    System.Threading.Thread.Sleep(700);
                    clearErrorMessageLine();
                    break;
                }

            case eErrorMessage.InvalidKeyPressed:
                {
                    clearErrorMessageLine();
                    System.Console.WriteLine(@"Invalid key pressed, to play enter column from 1 to {0}, to quit enter {1}", m_BoardViewColSize, k_QuitChar);
                    System.Threading.Thread.Sleep(700);
                    clearErrorMessageLine();
                    break;
                }

            case eErrorMessage.ColumnIsFull:
                {
                    clearErrorMessageLine();
                    System.Console.WriteLine(@"Column is full, please choose another column");
                    System.Threading.Thread.Sleep(700);
                    clearErrorMessageLine();
                    break;
                }

            case eErrorMessage.BoardIsFull:
                {
                    clearErrorMessageLine();
                    System.Console.WriteLine(@"Board is full, game finished by a draw");
                    System.Threading.Thread.Sleep(700);
                    clearErrorMessageLine();
                    break;
                }
        }
    }

    private void setCursorInErrorPosition()
    {
        System.Console.SetCursorPosition(0, m_BoardViewRowSize + 10);
    }

    private void clearErrorMessageLine()
    {
        setCursorInErrorPosition();

        for (int i = 0; i < System.Console.WindowWidth; i++)
        {
            Console.Write(" ");
        }

        setCursorInErrorPosition();
    }

    public void EndGame()
    {
        Ex02.ConsoleUtils.Screen.Clear();
        System.Console.WriteLine(
@"*************************
*       Thank you       *
*                       *
*       For Playing     *
*                       *
*       4 In a Row      *
*                       *
*************************" + System.Environment.NewLine);
        System.Threading.Thread.Sleep(2000);
    }

    public void Draw(int i_FirstPlayerScore, int i_SecondPlayerScore)
    {
        {
            Ex02.ConsoleUtils.Screen.Clear();
            System.Console.WriteLine(
@"*************************
*       It's a draw!      *
*                         *
*                         *
*  Player 1 Score: {0}      *
*  Player 2 Score: {1}      *
*                         *
*************************" + System.Environment.NewLine,
                           i_FirstPlayerScore,
                           i_SecondPlayerScore);
            System.Threading.Thread.Sleep(2000);
        }
    }
}
